IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

标签:C

共 71 篇相关文章

IT 累计浏览 33

soluna 外挂 C 模块

soluna 默认静态链接 Lua 虚拟机,导致无法直接外挂动态链接的 C 扩展,因为动态链接会引入多份 Lua 实现,引发运行时错误。根源在于 Lua 中全局空对象的静态引用机制:多份实现会生成多个空对象,运行时比较时出现不一致。虽然 Lua 5.4 后将空对象移入运行期结构以缓解问题,但作者仍强调应避免多份实现。Windows 平台因 DLL 必须编译时绑定所有符号,问题尤为突出。 为解决此问题,soluna 采用新方案:外部 C 扩展库链接代理模块 extlua.c,该模块不依赖 Lua 内部实现,而是利用 lua_getextraspace 宏注入 Lua C APIs。外部库需定义 extlua_init 入口函数,在其中调用 luaapi_init 注入 APIs,再通过 luaL_newlib 注册模块函数。soluna 的加载器通过创建临时虚拟机、传递 APIs 引用并复制入口表来完成动态加载。这种方法同时兼容动态库

IT 累计浏览 212

errno 的实现

POSIX标准对errno的定义从早期的外部变量演变为可修改左值的宏,旨在解决多线程环境下共享全局变量导致的错误码覆盖问题,确保线程安全。FreeBSD的具体实现采用函数指针间接调用模式:errno宏展开后会调用__error()函数获取int指针。在单线程场景下,__error()通过函数指针返回全局变量__libsys_errno的地址。当程序链接线程库libthr时,其构造函数会调用__set_error_selector,将函数指针切换至线程安全版本__error_threaded。该函数会检查线程初始化状态:若当前线程非初始线程,则返回该线程pthread结构体中独立的error字段地址;否则仍回退使用全局存储。这种设计既保证了主线程在线程库初始化前的可用性,又为每个工作线程提供了独立的错误存储。此外,libthr还通过弱符号引用提供了兼容路径,并将一系列可能阻塞的系统调用替换为线程化版本,以处理取消点等线程语义。

IT 累计浏览 4,413

一个 VLA (可变长度数组)的实现

这篇讲的是作者如何用C语言实现一个更实用、更安全的可变长度数组库。C99引入的VLA特性因安全问题已被MSVC和Linux内核相继放弃,但在日常开发中,变长数组的需求依然存在。现有的通用方案,如C++的std::vector或简单的void*实现,在类型安全、性能(堆内存分配)和与特定运行时(如Lua)的集成方面各有不足。 作者的方案核心是将VLA拆分为抽象的“句柄”和类型化的“访问器”。通过`vla_using`宏,在栈上创建一个指向实际数据的原生指针作为访问器,同时关联句柄,从而在保证类型安全和原生数组访问性能的同时,提供了清晰的API。为兼顾临时使用与持久引用的不同场景,方案统一了栈上缓存与堆分配的切换逻辑。 更巧妙的一点是,作者针对与Lua交互的场景,实现了利用Lua GC管理内存的第三种模式:小块内存直接分配在C栈上,大块内存则转为Lua临时userdata,随函数退出自动回收,省去了手动清理的麻烦。整个实现展示了如何在C语言的限制下,通过宏技巧和分层设计,构建出一个既高效又贴合实际工程需求的通用数据结构。

IT 累计浏览 3,960

Linux下互斥量加锁与解锁操作的C代码实现

这篇讲的是在多线程编程中如何安全地保护共享资源。作者从一个常见问题出发:当多个线程需要修改同一全局变量时,必须确保操作的原子性,否则容易引发不可预知的错误。 文章核心是演示一套完整的互斥量加锁与解锁C语言实现。作者没有停留在理论,而是直接给出了可运行的代码文件“LockAndUnlock.c”。其中,自定义的`MutexLock`函数封装了`pthread_mutex_timedlock`,并通过`gettimeofday`获取系统时间,巧妙地计算出带超时(5秒)的绝对等待时间,避免了线程可能被永久阻塞的风险。`MutexUnLock`函数则简洁地封装了解锁操作。 代码结构清晰,包含了宏定义、函数声明和完整的错误处理逻辑。文章最后还附上了在Linux下的具体编译命令(gcc -pthread)和运行结果,形成了一个从问题、方案到验证的闭环。对于需要在C程序中使用POSIX线程互斥机制的开发者来说,这套封装好的函数可以直接作为参考或API使用。

IT 累计浏览 2,981

socket消息流程介绍及其C代码实现

这篇讲的是如何用C语言完整实现一个Linux下的TCP服务器,清晰地展示了socket通信从创建到结束的全过程。作者通过一个具体的程序示例,逐步拆解了socket编程的核心流程:首先创建socket并绑定指定的IP与端口,其中还设置了SO_REUSEADDR选项来处理端口复用;接着启动监听,循环接受客户端连接;最后接收指令、执行操作并返回结果。代码中对每个环节都加入了错误处理和状态反馈,使得流程健壮且逻辑清晰。文末还附上了makefile编译脚本和通过工具测试的运行结果,让读者能直观地看到“发送-接收-响应”的完整交互。对于想要理解底层网络编程细节的开发者来说,这篇从代码到理论再到实践的梳理,提供了一个非常扎实的参考范例。

IT 累计浏览 9,187

一个大二学生有关如何成为一名软件工程师的疑问及答复

这篇文章记述了一位资深工程师对一位迷茫的大二学生的答复。学生困惑于课堂所学(如C和Java)与实际软件工程师岗位要求之间的差距,不知如何规划发展路径。 作者首先共情了这种校园学习与产业需求脱节的普遍感受,并指出“终身学习”对软件行业的必要性。他清晰地拆解了软件工程师的核心工作:一是开发,涵盖需求评审、文档编写、代码实现、测试与版本提交;二是维护,包括修复测试bug、解决现场问题及制作升级包。 基于此,作者总结了工程师需具备的能力:硬能力(专业基础、编码与文档能力)和软能力(分析解决问题、沟通、抗压等)。对于在校学生,他建议通过多动手编程、参与项目、社团活动和假期实习来有意识地培养这些能力,制定个人化的学习计划。 回复并未停留在说教,而是通过拆解职业全景,将“如何成为软件工程师”这个模糊问题,转化为具体、可行动的能力培养清单。希望这位同学的疑问,也能给其他迷茫中的读者带来一些方向感。

IT 累计浏览 4,989

struct与class区别联系

这篇讲的是C和C++中`struct`这个看似相同的关键字,其实内核大不相同。作者开篇就指出了核心区别:C中的`struct`是“原生”的,仅仅用来将一组属性打包成一个整体,没有任何面向对象(OO)的特性。而C++中的`struct`则是在此基础上做了深度扩展,它完全兼容C的用法,但更重要的是具备了OO特性——事实上,C++中`class`能干的事情,`struct`几乎都能干,包括继承和多态。 文章通过一个直观的代码示例验证了这一点:如果在纯C环境下(例如用GCC的C模式编译),在`struct`内部直接定义成员函数会导致编译报错;但同样的代码在C++中则毫无问题。这生动地说明了“原生”与“扩展”的差异。 那么,在C++中`struct`和`class`到底还有何区别?唯一的、关键的不同在于默认的访问权限:`struct`默认是`public`,而`class`默认是`private`。这个细微差别决定了代码风格和设计意图。通常,我们用`struct`来封装纯数据的聚合体,而用`class`来定义那些需要隐藏实现细节、提供接口的抽象数据类型。这篇小文通过对比和代码解析,清晰地帮你厘清了这个C++程序员常会遇到的疑惑。

IT 累计浏览 4,215

一个“蝇量级” C 语言协程库

这篇讲的是如何用不到100行代码在C语言里实现协程。作者从协程的背景讲起,对比了它和线程在“生产者-消费者”模型中的差异,指出C语言由于依赖栈帧实现函数调用,缺乏原生yield语义,直接实现对称协程比较麻烦。 文章核心介绍了开源库protothreads。这个库“蝇量级”到所有代码都在5个头文件里,每个协程只需2字节内存开销,非常适合资源敏感的嵌入式场景。作者还追溯了作者Adam Dunkels的其他知名作品,暗示其工业级可靠性。 实现的关键在于一个C语言“奇技淫巧”:利用switch-case语句的穿透特性和`__LINE__`宏,模拟了程序计数器的保存与恢复。通过将return语句嵌入case标签,并在函数入口用一个switch跳转到上次的挂起点,就用纯C语法实现了协程的挂起与恢复。文章最后展示了如何用宏封装出Begin/Yield/End这样的简洁接口,让开发者能写出类似Python生成器的代码结构。

IT 累计浏览 2,035

linux中c语言errno的使用

这篇讲的是Linux环境下C语言编程中errno的实战用法。作者从errno的基本特性切入,强调了它只在函数返回异常时才有意义,避免读者陷入盲目检查的误区。 文章的核心部分对比了两种将错误码转换为可读信息的方式:strerror函数允许将错误描述拼接到自定义输出中,适合构建详细的日志或用户提示;而perror函数则更直接,一行代码就能将错误消息附带到标准错误流。作者还提醒,并非所有库函数(如gethostbyname)都会设置errno,这是个容易忽略的细节。 针对errno在多线程编程中的可靠性,文章通过剖析errno.h头文件的宏定义,明确指出在启用可重入库(_LIBC_REENTRANT)的现代环境中,errno已被实现为线程局部变量,保证了安全性。最后附带的一段检测代码,让读者能轻松验证自己编译环境的相关定义是否生效。

IT 累计浏览 7,091

Linux C语言编程学习材料

这篇整理了Linux C语言编程从入门到精通的完整学习路径。它把资源清晰地划分成三个阶段:快速入门用《Linux C编程一站式学习》打基础,长期深耕则推荐了C Primer Plus、经典数据结构教材以及APUE等“圣经”级著作。最硬核的部分在于高级网络编程资源,不仅覆盖了《Linux高性能服务器编程》等通用指南,还深入到Apache、Nginx的模块源码分析,以及MySQL内核、Redis实现剖析等具体高性能组件的“深水区”。 作者显然意图为志在开发高性能后端的工程师,构建一个从语言基础、系统编程到具体中间件实现的扎实知识栈。资料列表兼顾了经典纸质书与电子文档,尤其像Redis源码分析系列博客、PHP内核手册等,提供了贴近工程实践的切入点。整份清单像一份精心设计的“技术地图”,让学习者能按图索骥,逐步构建起支撑大规模服务的底层能力体系。

IT 累计浏览 2,896

通过call_usermodehelper()在内核态执行用户程序

这篇讲的是如何在 Linux 内核中“跨界”执行用户空间的程序。作者从内核开发者常遇到的需求出发,介绍了 `call_usermodehelper()` 这个内核API。 文章指出了它的核心作用:让运行在内核态的代码(比如模块或驱动)能够主动启动并执行一个用户空间的可执行文件或系统命令,就像在 shell 里敲命令一样。作者还提到了一个关键的实现细节:这个函数最终会调用内核的 `do_execve()`,这和用户态的 `execve()` 系统调用在底层“殊途同归”,但调用路径和上下文完全不同。 为了说明如何使用,文章给出了一个加载函数的代码片段示例,演示了调用该API的基本结构。对于需要在内核逻辑中动态触发外部脚本或工具进行日志收集、环境配置等场景,这个接口提供了一条直接通道,理解它有助于编写更灵活的内核模块。

IT 累计浏览 6,225

C语言结构体里的成员数组和指针

这篇讲的是C语言中结构体成员访问的一个经典误解。作者从微博上一道让程序崩溃的代码题出发,拆解了其中隐藏的底层机制。题目里结构体包含零长度数组 `char s[0]`,通过空指针 `f.a` 访问成员 `s` 时,程序没在 `if` 判断崩溃,却在之后的 `printf` 处崩溃。 文章深入剖析了根源:在C语言里,访问结构体成员本质上是进行“基址 + 编译时确定的偏移量”计算。对于数组成员 `s`,`f.a->s` 操作得到的是这个数组的相对地址(通过 `lea` 指令实现),所以即使 `f.a` 是空指针,计算出的地址(如 0x4)也不会立刻引发崩溃。但如果把 `s` 声明为指针 `char *s`,`f.a->s` 则会解引用这个空指针(通过 `mov` 指令),程序就会在判断条件处直接崩溃。作者还澄清了零长度数组是编译器扩展(如GCC),常用于实现“柔性数组”以分配不定长数据。 文章强调,理解变量的地址本质、成员访问的偏移计算以及数组名与指针的操作区别,是避免这类“坑”并掌握C语言内存模型的关键。

IT 累计浏览 5,791

libcurl中使用curl_easy_getinfo 产生段错误分析

这篇文章从一个实际的开发案例出发,分享了在 libcurl 中使用 `curl_easy_getinfo` 函数时遇到的隐蔽陷阱。作者在编写 HSF 代理程序时发现,将 `CURLINFO_RESPONSE_CODE` 的返回值写入 `int` 类型变量会导致程序段错误,而使用 `long` 类型则一切正常。 问题的根源在于 `curl_easy_getinfo` 的实现采用了可变参数机制。库内部会按照 `long*` 类型来接收并写入数据,而编译器并不会检查调用者传入的指针类型是否匹配。在 64 位系统中,`long` 类型通常为 8 字节,`int` 为 4 字节,这导致函数调用时向较小的栈空间写入了过多数据,从而破坏了栈帧,引发了段错误。作者通过一段模拟代码清晰地复现并验证了这一过程。 文章的价值不仅在于指出了这个具体的 API 使用陷阱,更提醒开发者在面对 C 风格可变参数函数时需格外谨慎。类型声明的一字之差,在特定平台上可能演变成难以调试的内存破坏问题,这要求我们对底层数据模型和函数契约保持清晰的认知。

IT 累计浏览 6,152

如何实现一个malloc

很多用C语言的程序员都熟悉 `malloc` 这个函数,但它既不是C关键字也不是系统调用,其背后的实现原理常被忽略。这篇讲的是如何从零开始实现一个简易的 `malloc`,帮助读者理解内存分配器的核心思想。 文章从现代操作系统的内存管理基础讲起,清晰梳理了虚拟内存、页表、Linux进程内存布局等关键概念,特别聚焦于堆内存管理和 `brk`/`sbrk` 系统调用。作者先给出了一个只增不减的“玩具实现”,直观展示如何用 `sbrk` 划拨内存,进而引导读者思考如何设计元数据、管理空闲块以实现真正的分配与释放。 虽然这个为了教学而简化的实现效率不及 glibc 中的生产级代码,但它与真实 `malloc` 的实现原理一致。对于想深入理解内存管理、打破 `malloc` 黑盒的开发者而言,这篇文章提供了一条清晰且可动手实践的路径。

IT 累计浏览 3,474

Linux内核代码中的脏话统计

这篇讲的是作者从一个已停更的“the linux kernel fuck count”项目获得灵感,对Linux内核的C、H及汇编源代码进行了一次系统的脏话统计分析。作者按版本号,分别从脏话的绝对数量和代码行的脏话密度两个维度绘制了图表。 数据呈现了一个有趣的趋势:从2.4版本开始,脏话的绝对数量显著攀升。然而,考虑到同期内核代码总量也在激增,折算下来,平均每行代码的“脏话密度”反而是在下降的。文章坦诚地分享了统计方法的局限,比如会将词中包含的词也计入,以及受FreeBSD正则引擎内存泄漏的影响未能优化。 作者最终开源了统计脚本,但也自嘲其代码质量混乱。这本质上是一次用独特视角审视开源社区文化的趣味实践,既看到了开发者情绪的外露,也反映了代码库膨胀带来的稀释效应。

IT 累计浏览 4,751

C语言全局变量那些事儿

这篇讲的是C语言全局变量多重定义的“危险”与“微妙”行为。作者从全局变量在不同视角(程序员、编译器、计算机)下的不同含义切入,通过三个递进的代码示例,深入剖析了编译链接器对“强符号”与“弱符号”的解析规则。 文章揭示了一个常被忽略的隐患:C语言实际上“允许”全局变量的多重定义(只要不是多个强符号),这可能导致内存被意外覆盖。示例中,同一个变量名在不同文件里可以是结构体或整型,却链接到同一块内存,其初始化值会发生覆盖。作者进一步展示了在多进程(fork)环境下,这种行为如何与操作系统的“写时拷贝”机制相互作用,使得不同进程的同一虚拟地址映射到不同的物理内存,从而产生隐蔽的状态差异。 最后,通过将代码编译为静态库链接,作者验证了这种行为在静态链接下依然存在。这篇文章的价值在于,它用具体而震撼的运行结果,将抽象的链接原理和潜在风险可视化,提醒开发者谨慎对待全局变量,尤其是非static限定的全局变量。

IT 累计浏览 6,409

C的那些事儿

这篇讲的是C语言在Linux生态中的实践心得与工具推荐。作者从C语言作为许多人的编程启蒙谈起,指出尽管学习路径在演变,但C语言在Linux系统编程领域——如操作系统、数据库等高性能场景——仍占据着核心地位。 文章重点分享了提升C语言“品味”的具体路径。一方面推荐使用Source Insight这类静态分析工具,通过代码跳转、关系图等功能,在庞大的开源代码库中理清脉络;另一方面更推崇借助gprof和cgprof进行动态性能分析,生成可视化的函数调用图,直观展示函数调用次数与时间占比,以真实运行数据替代主观猜测。 作者还特别赞赏了Linux的管道(Pipe)机制,它让不同程序能够无缝衔接,实现了二进制层面的高效代码复用,这比单纯的语句级复用更为精妙。文中以通过管道串联grep、awk等命令为例,展现了这种设计的优雅与强大。 整篇文章并非泛泛而谈,而是结合了作者手边的函数手册、亲测有效的工具链(从编译选项`-pg`到生成调用图的完整流程),以及对Linux设计哲学的具体感受,为想在开源世界中精进C语言的读者,提供了一条清晰、可操作的实践思路。

IT 累计浏览 4,364

谁能看明白这幅Java、PHP、C、Ruby语言相互吐槽的搞笑图片都说的是什么?

这是一篇围绕一张经典编程语言“鄙视链”梗图展开的趣味讨论。作者分享了这张将Java、PHP、C和Ruby四种语言拟人化,让它们“互相吐槽”的搞笑图片,并坦言自己研究很久也未能完全参透图中每一个比喻的深意。 文章详细列出了各语言粉丝眼中的自己与“对手”:Java用户自诩稳定全能,却视PHP为小儿科;PHP爱好者认为Ruby复杂难懂,而自己只是“好用的工具”;Ruby拥趸则觉得Java太商业,PHP是“假超人”。有趣的是,图中为C语言粉丝留下了大量问号,这恰恰成了最大的悬念——那位追求极致性能的“老大哥”,究竟如何看待这些后辈们? 作者将图表和个人解读一并呈现,并非寻求标准答案,而是以一种轻松的方式邀请读者共同“破译”这些技术调侃背后的文化密码。无论你是哪种语言的开发者,都能从中会心一笑,或许还能在评论区贡献出更犀利的解读。

IT 累计浏览 2,931

C语言打开文件的模式

这篇讲的是作者在处理BMP图像文件时遇到的一个经典坑。他在编写一个读取并保存BMP文件的程序时,发现输出的文件总比原文件大3个字节,而且图像内容完全错乱。问题出在哪里呢? 经过排查,根源在于打开文件时使用了`fopen(filename,"r+")`这种默认的文本模式。在文本模式下,C语言的文件读写会进行换行符(`\n`)与“回车+换行”(`\r\n`)的自动转换。而BMP文件作为二进制文件,其数据流中恰好包含十六进制值`0a`(等于换行符的ASCII码),这个值在颜色表中出现了三次。结果,每次读取时程序都把`0a`误当作换行符并扩展成两个字节,导致数据读取溢出;写回时又将两字节序列压缩为一字节,最终使得文件大小永久性地多出了3个字节,破坏了图片结构。 解决方法非常直接:将文件打开模式改为二进制模式`"wb"`。这个小bug提醒我们,只要操作的不是纯文本文件,就必须明确使用二进制模式(如`"rb"`、`"wb"`),以避免底层编码转换带来的隐秘错误。

IT 累计浏览 5,244

Tips of Linux C programming

这篇文章分享了Linux内核和GNU C中一些不那么为人所知却非常实用的编程技巧。作者从链表的非常规定义讲起,展示了如何将链表节点嵌入到数据结构中,并利用`container_of`宏从节点地址反推出宿主结构体,这种方法比传统教科书定义更灵活优雅。 随后,文章深入到编译器与硬件层面:介绍了用`likely`/`unlikely`宏提示编译器优化分支预测,减少流水线冲刷;演示了通过内联汇编和`lock`指令前缀实现原子加法,保证多处理器环境下的数据一致性;还探讨了GNU C特有的零长度数组特性,用于在运行时动态分配结构体尾部的变长数组。最后,简短提到了三目运算符`a = x ? : y`这种简洁的省略写法。 这些技巧都源自真实的内核开发或GCC特性,能帮助C程序员写出更高效、更地道的代码。文章穿插了关键的代码片段和原理剖析,对希望提升底层编程技巧的读者很有启发。